﻿namespace UCSD_BioLit
{
    #region Namespace

    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Linq;
    using Microsoft.Office.Core;
    using MSWord = Microsoft.Office.Interop.Word;

    #endregion
    
    /// <summary>
    /// OntologyTermBase is the base class for classes implementing term recognition and context menu control creation.
    /// </summary>
    public abstract class OntologyTermBase
    {
        /// <summary>
        /// Class name for exception title.
        /// </summary>
        private const string ExceptionString = "OntologyTermBase";

        /// <summary>
        /// Separator used to create the Context menu control tag id.
        /// </summary>
        protected readonly string Separator = "||";

        /// <summary>
        /// Tag ID for "View Term in OntologyBrowser" option.
        /// </summary>
        protected readonly int OntologyBrowserTagId = -1;

        /// <summary>
        /// Tag ID for "Apply Same mark-up to all Instances of Term" option.
        /// </summary>
        protected readonly int MarkAllInstancesTagId = -2;

        /// <summary>
        /// Collection to store the recognized terms.
        /// </summary>
        protected Dictionary<string, RecognizedTermProperties> _termCollection = new Dictionary<string, RecognizedTermProperties>();

        /// <summary>
        /// Initializes a new instance of the OntologyTermBase class.
        /// </summary>
        public OntologyTermBase()
        {
            ContextMenuHelper.Instance().CreateMenuControlsEvent += this.CreateControls;
            ContextMenuHelper.Instance().RecognizeTermsEvent += this.Recognize;
        }

        /// <summary>
        /// Gets the recognized term collection.
        /// </summary>
        public Dictionary<string, RecognizedTermProperties> TermCollection
        {
            get
            {
                return this._termCollection;
            }
        }

        /// <summary>
        /// Virtual method to Recognize terms. This needs to be overridden in the derived class.
        /// </summary>
        /// <param name="sender">The sender of the event.</param>
        /// <param name="e">TermRecognitionEventArgs object with details about the current selection and cursor position.</param>
        public abstract void Recognize(object sender, TermRecognitionEventArgs e);

        /// <summary>
        /// Virtual method to create context menu controls. This needs to be overridden in the derived class.
        /// </summary>
        /// <param name="sender">The sender of the event.</param>
        /// <param name="e">The EventArgs.</param>
        public abstract void CreateControls(object sender, EventArgs e);

        /// <summary>
        /// For a given selection range and text, finds occurances of the term within the given range.
        /// Adds matches to the TermCollection list if they are near the current cursor position.
        /// </summary>
        /// <param name="selectionRange">The Range of the current selection.</param>
        /// <param name="ontologyTerm">The term that needs to be searched within the current range.</param>
        /// <param name="cursorPosition">The current cursor position.</param>
        /// <param name="isSelection">
        /// Indicates whether a specific range of text was selected by the user.
        /// If true, terms are added to the list regardless of the cursor position.
        /// </param>
        protected void AddTermsInRange(MSWord.Range selectionRange, string ontologyTerm, int cursorPosition, bool isSelection)
        {            
            object findText = ontologyTerm;
            object matchCase = false;
            object matchWholeWord = true;
            object forward = true;
            object missing = System.Reflection.Missing.Value;

            do
            {
                try
                {
                    // Find the specified text within the current range.
                    selectionRange.Find.Execute(ref findText, ref matchCase, ref matchWholeWord,
                        ref missing, ref missing, ref missing, ref forward, ref missing, ref missing,
                        ref missing, ref missing, ref missing, ref missing, ref missing, ref missing);

                    // Check if the word was found. If it is near the current cursor position or was explicitly selected by the user, add it to the list.
                    if (((selectionRange.Start <= cursorPosition && selectionRange.End >= cursorPosition) || isSelection) &&
                        selectionRange.Find.Found)
                    {
                        RecognizedTermProperties termProperties = new RecognizedTermProperties
                        {
                            RecognizedTerm = selectionRange.Text,
                            TermRange = selectionRange.Duplicate
                        };

                        // Check if the term has already been added.
                        if (!this._termCollection.Values.Any(delegate(RecognizedTermProperties props)
                        {
                            return props.TermRange.IsEqual(termProperties.TermRange);
                        }))
                        {
                            // Add the term to the list so that appropriate controls can be created.
                            this._termCollection.Add(Guid.NewGuid().ToString(), termProperties);
                        }
                    }
                }
                catch (Exception ex)
                {
                    Debug.WriteLine(ex.Message, ExceptionString);
                }
            }
            while (selectionRange.Find.Found);
        }
    }
}
